Google Earth Engine is a cloud-based platform hosted by Google that uses Google’s high-performance and intrinsically parallel computing system that subdivides computations across the Google computational infrastructure, accelerating computational processes (Gorelick et al., 2017). It has been primarily designed to process and analyze satellite imagery and geospatial datasets, making computationally demanding workflows more accessible for research, education and other nonprofit use at no user cost. Thus, it is totally free.
Up to 2008, using satellite imagery for data analysis was expensive. In 2008, however, the United States Geological Survey (USGS) opted for a free and open policy on the use of the most popular satellite, Landsat (Woodcock et al., 2008). The release of Landsat catalog to be freely use by the public opened enormously the possibilities to expand earth research (Woulder et al., 2019). However, with so much data available, the limitation became data processing rather than data gathering. Personal computers are generally limited in computation power to analyze the amount of data that a composite of Landsat images require and data storage is also limited. Supercomputers can do that job, but access them is only optional for researchers working in certain institutions. Google opened the option for all users with internet access to operate on a high computing infrastructure through the new platform, Google Earth Engine.
When the increase of satellite data accessibility is combined with free access to fast computing processes, researchers and students have the possibility to carry out extremely high computing demanding projects in matter of minutes. For ecologists, this has opened the horizons of research possibilities in unthinkable ways. And while lot of attention has been put into the use of Google Earth Engine for remote sensing scientists (Tamimina et al., 2020), the tool has not reached yet the spatial ecological field in its full capacity. In this tutorial, we explore the different applications that this platform provides to analyze data for projects focused on spatial ecology.
This workshop is intended to provide an introduction to GEE for participants in the fields of ecology, conservation, and wildlife biology. The course will begin by covering the basics of GEE and JavaScript coding before moving on to spatial data manipulation and analyses. Participants need no prior experience working with GEE, but should have a basic understanding of GIS data and concepts, such as raster (including stacks or multi-band images), vectors, projections, and spatial resolution. This course is a continuation of a previous year’s module “Introduction to Geographic Information Systems (GIS) and Wildlife Population Monitoring in R.” At the conclusion of the course, we will combine GEE workflows with population analysis in R, with a goal of creating a community of practitioners that have the skills necessary to expand upon science-based monitoring of wildlife populations across Kenya.
You are expected to use your own computer for all exercises. By the end of this course, you will be able to:
Here we present a tutorial that will guide you on the use of different model techniques for data analysis. The main goal is that after the workshop, you will be able to continue practicing with this tutorial, and master your skills to analyze your data using Google Earth Engine interface.
The tutorial is organized in 5 main sections. Section 1 is an introduction to Google Earth Engine and the JavaScript language we use to code. Section 2 will guide you on the use of images and geometries, or raster and vector data. Section 3 is an opportunity to lonk GEE with other interfaces like R to make visualizations of your data. Section 4 is an introduction to the implementation of Species Distribution Models with GEE. Section 5 will bring us back to Distance Sampling and the use of covariables extracted with GEE in the modeling process.
All that code is available here.
Jared Stabach, PhD., Ecologist, Conservation Ecology Center, Smithsonian’s National Zoo and Conservation Biology Institute, Front Royal, VA, USA. Email: stabachj@si.edu
Grant Connette, Ph.D., Ecologist, Conservation Ecology Center, Smithsonian’s National Zoo and Conservation Biology Institute, Front Royal, VA, USA. Email: connetteg@si.edu
Ramiro D Crego, Ph.D., Lecturer in Zoology/Animal Ecology, School of Biological, Earth & Environmental Sciences - Environmental Research Institute, University College Cork, Ireland. Email: rcrego@ucc.ie
Gorelick, N., Hancher, M., Dixon, M., Ilyushchenko, S., Thau, D., & Moore, R. (2017). Google Earth Engine: Planetary-scale geospatial analysis for everyone. Remote Sensing of Environment, 202, 18–27. https://doi.org/10.1016/j.rse.2017.06.031
C.E. Woodcock, R. Allen, M. Anderson, A. Belward, R. Bindschadler, W. Cohen, F. Gao, S.N. Goward, D. Helder, E. Helmer, R. Nemani, L. Oreopoulos, J. Schott, P.S. Thenkabail, E.F. Vermote, J. Vogelmann, M.A. Wulder, R. Wynne. 2008. Free access to Landsat imagery. Science, 302 (5879), p. 1011
Michael A. Wulder, Thomas R. Loveland, David P. Roy, Christopher J. Crawford, Jeffrey G. Masek, Curtis E. Woodcock, Richard G. Allen, Martha C. Anderson, Alan S. Belward, Warren B. Cohen, John Dwyer, Angela Erb, Feng Gao, Patrick Griffiths, Dennis Helder, Txomin Hermosilla, James D. Hipple, Patrick Hostert, M. Joseph Hughes, Justin Huntington, David M. Johnson, Robert Kennedy, Ayse Kilic, Zhan Li, Leo Lymburner, Joel McCorkel, Nima Pahlevan, Theodore A. Scambos, Crystal Schaaf, John R. Schott, Yongwei Sheng, James Storey, Eric Vermote, James Vogelmann, Joanne C. White, Randolph H. Wynne, Zhe Zhu. 2019. Current status of Landsat program, science, and applications, Remote Sensing of Environment, 225, 127-147, https://doi.org/10.1016/j.rse.2019.02.015.
Haifa Tamiminia, Bahram Salehi, Masoud Mahdianpari, Lindi Quackenbush, Sarina Adeli, Brian Brisco. 2020. Google Earth Engine for geo-big data applications: A meta-analysis and systematic review. ISPRS Journal of Photogrammetry and Remote Sensing, 164, 152-170, https://doi.org/10.1016/j.isprsjprs.2020.04.001.
Google Earth Engine (GEE) is an online Integrated Development Environment (IDE) for rapid development and visualization of complex spatial analyses using the JavaScript or Phyton coding languages, allowing users to conduct data heavy analysis. While computer coding can sound challenging for ecologists, researchers, students or managers that have never used code before, it is rather simple and opens the spectrum of data analysis enormously. The integrated platform that Google Earth Engine provides allows not only traditional remote sensing scientists, but also a much broader spectrum of users to run spatial analysis, without the need to have deep expertise on the use of supercomputers.
The GEE platform has the main advantage of providing access to:
Petabytes of remote sensing imagery and other spatial products;
High-speed parallel processing and machine learning algorithms;
A library of Application Programming Interfaces (APIs) with development environments
These three features enable the user to visualize and analyze a bast range of geospatial data fast and for free, without the need to access a supercomputers (Gorelick et al., 2017).
Google Earth Engine Application Programming Interface (API) can be run in popular coding languages, such as JavaScript and Phyton. In this tutorial we will focus on the use of JavaScript. In this first section we will explore the basics of Google Earth Engine, from understanding how to set up an account, to understand the interface to explaining the basics of JavaScript coding language.
The first step to start using Google Earth Engine is to set up a Google Cloud project. In this link you will find the instructions. If you are working in research or for non-profit organizations, you can register a free or charge non-commercial project.
Once your Google Earth Engine account is set, you will be able to aces the Code Editor to start working on it in your browser at https://code.earthengine.google.com/.
The Google Earth Engine interface is an interactive web-based environment for data analysis. It has four main panels (Fig. 1):
The Code Editor panel to write JavaScript
code.
The Left panel for managing and hosting programming
scripts, accessing Google Earth Engine documents and managing and
uploading external data sets such as shapefiles.
The Right panel with tabs for printing, queering
maps and exporting products or long-running tasks.
The Interactive map panel for visualizing maps and
outputs.
Additionally, on top of the interface is the search tab,
that allows you to search and find data sets and places. At the top
right is a help menu that links to a user guide, help forum
and other types of support (Fig. 1).
Each main panel has a variety of elements that will explore more in detail (Fig. 2). Among this are:
The Docs tab or documentation tab
The Scripts tab or Git-based Script Manager
The Assets tab or asset manager
The search bar
Several buttons in the script panel to save, run and manage scripts
The Inspector tab or interactive map query
The Console tab
The Tasks tab
Geometry drawing tools in the interactive map panel
Let’s explore the different panels and elements more in detail.
The code editor panel is the area where you will write the JavaScript code to perform all type of analysis. Above the code editor panel are buttons to save your current script, run it, and clear the script. The Get Link button (Fig. 3) generates a unique URL for the script in the address bar that allows you to share the script and/or open it in a new browser tab. This is a unique characteristic of GEE that allows you to share your work and make it reproducible by any other user with a GEE account.
The drop-down button at the right of the Get Link button has the option to “Manage Links.” Clicking this button opens a new tab that allows you to access, remove and download previously generated script links. If you choose to download the script, this will be saved to your personal computer as a zipped folder containing a .txt file with the code.
Next to the Get Link button is the Save button. This button allows you to save the script as a new script and subsequent updates.
Next is the Run button that allows you to run the script. The drop-down button allows you to run the script with profiler (See Profiler tab below).
To the right, the Reset button will reset all tabs on the right panel. The drop-down button will allow you to clear the script to start a new one.
Next is the Apps button. This opens the App window display that we will explore later on the tutorial.
Finally, there is a Settings button that allows you to activate options to underline code suggestions and to auto-complete pairs such as quotation marks, parentheses and brackets. You can activate or deactivate these two options depending on your coding preferences.
The Docs tab (Fig. 4) contains the entire JavaScript API documentation developed by Google. Here, you will find all the information necessary to understand functions. We recommend you to get familiar with this tab as it gets really useful for writing scripts and running more complicated analysis. You can use the search tab to filter methods and find specific functions.
When clicking on any build in function, you will find a description of the function, the arguments that the function takes, and the what the function returns.
The Scripts tab is where private, shared and all script examples hosted by Google are stored. The Owner level stores all your private scripts, the ones that only you can see and edit. The Writer level stores scripts that has been shared (access granted) with you by another user. Sharing repositories and scripts can be really useful to work on collaborative projects. Once someone has added you to a repository, you can add new scripts, modify existing scripts, and manage access to the repository for other users. Of course, you can create and share repositories and scripts with other users. The Reader level contains scripts for which you have read access granted by the script owner, but you cannot edit the scripts. The Example level has script examples created and managed by Google, that are really good for some basic operations. Take a look at the scripts available in this folder. The Archive level is for scripts that users have access to read but have not been migrated by their owner from an older version of the Script Manager. The search tab allows you to search for scripts within all these levels.
The New button allows you to create a new repository in the Owner folder or to create folders and files within existing repositories. It is a good practice to keep your repositories and scripts organized. Give folders and scripts proper names, something that you will be able to recognize after several months of not working on them. Your brain can forget things pretty quick. Once you have created your repositories and scripts, they can be renamed and deleted by the Rename and Delete buttons that appear at the right on the script while positioning the pointer on top of the script. Scripts can be dragged and dropped into new folders or copied into new repositories. The Revision History button allows to compare with previous versions and recover them. Always important in case that you delete something by mistake. Similarly, positioning the pointer on top a repository allows to delete and access the history of the repository. The Configure button allows you to manage access to the repository. As we said before, you can grant access to other users to your repository. Repositories can be configured to be accessed using Git, so you can manage and edit your scripts outside the Code Editor, or sync them with an external system like Git Hub (Fig. 6).
For this tutorial we have created a repository that you can access in the reader level.
The Asset tab allows you to upload and manage your own image assets, shapefiles, and CSV files (Fig. 7). You can click on NEW to upload new assets from your personal computer. We will learn how to do this with later examples in the following sections. For now, it is good to get familiarized with the different parts of the interface.
The search bar located in the top of the Code Editor that says ‘Search places and data sets…’ allows you to search the GEE data archive. Here you can type different keywords of data products, sensors or places, to filter the data base. If you search for a place, clicking on the place will make it visible in the map. If you search for a data product, such as Landsat, you will see a detailed description of each one. Click Import to add the data product into your script as an import (Fig. 8), which will appear at the top of the script. It is also possible to import data products using code, so you do not have to search and click the Import button if you want to reuse the script for another work. Additionally, you can see the JavaScript code to import any product by clicking on the copy button next to the code and pasting it into your script. You can delete imports you have added to your script by positioning your pointer next to the import and clicking the delete icon.
The inspector tab allows you to interactively query the different map layers you have displayed (Fig. 9). When you clicking on the tab, the pointer becomes a crosshair, meaning the function is activated. You can click now on any part of the map and the tab will display the location and values of all layers on the map. This can be useful to inspect the values of your layers across different locations when performing an analysis or when inspecting different layers in any area of interest.
The console tab is located next to the inspector tab (Fig. 9). This tab is where all the information you print on the code will be displayed. We will see later how to print information when using JavaScript. For now, it is enough to remember that this is the tab in which you will interact with the data and where you can display information and results from operations. Those can be text, objects or charts. This console tab is interactive, meaning that you can expand printed objects to get more details about them. We will start to use this tab in the next section when you start writing your first JavaScript code.
The tasks tab is located next to the console tab. This tab will display long running tasks when exporting products from your analyses. Those could be images or feature collections. Once the export was called from the code editor, you will need to run it by clicking the run button next to the exported task. A dialog window will appear, which allows you to configure your export. Once you have run the task, the status will display. Tasks can be canceled while running by clicking on the spinning icon next to the task.
The profiler is a tab that will appear next to the task tab on the right upper panel when you run the script with the run with profiler option (or the shortcuts, holding down Alt for PC or Option for Mac, and clicking run, or pressing Ctrl+Alt+Enter/control+option+return). The tab displays information about CPU time, memory consumed and other computation parameters by the algorithms run and the display of all layers in the map. This can be useful to understand why a script is running slow or failing due to memory limitations. Not too important at the beginning, but something to keep in mind for when you start to me more advanced with Earth Engine analyses. Each row in the profiler tab corresponds to each algorithm, computation, or asset loaded as described in the Description column. The count column indicates the proportional number of times the operation was invoked, and the compute column indicates the CPU time taken by the operation. The Peak Mem column indicates the maximum memory used on any single computation node for the operation. The Current Mem appears only when there is an error given memory limitation and shows the amount of memory that was in use on any single compute node when the error occurred.
At the top-right corner of the interactive map panel is the layer manager that allows you to adjust the display of all layers you added to the map. From here you can activate and deactivate layers display and adjust its transparency with the slider. By clicking the setting icon, you can adjust the visualization parameters of each individual layer (Fig. 10). From here you can customize the stretch of pixel value display, adjust opacity (transparency), adjust gamma display and change the color palette. You can manually choose different colors. Click Apply button to apply the new visualization parameters to the current display. You can also use the Import button to load the new visualization parameters object as a new variable in the imports section of your script that you can use to display other layers on the interactive map.
At the right of the layer manager are buttons to choose different map backgrounds: Google street view, terrain view or satellite view with the option to activate labels. Another icon allows you to get into a full-screen view.
In addition to images, you can import geometries (i.e., vector data in Earth Engine language) to your script by drawing them on the map. To create a geometry, use the geometry drawing tools in the upper-left corner of the interactive map panel. There are four different geometries you can create: points, lines, polygons, and rectangles. When using the drawing tool, any geometry created will automatically create a new geometry layer in the import section of your script. Place the pointer on the Geometry Imports button and then click the +new layer link. Then choose the type of geometry you want to draw and start clicking on the map to draw your geometries. In the case of polylines and polygons, double click to finalize the drawing. Geometry display on the map can be activated and deactivated from the Geometry Imports section. We will learn more about geometries in the second section of this tutorial. You can delete geometries by activating them using the hand tool, and pressing Delete on your keyboard or by directly clicking the delete button on the Imports section of the script.
You can configure the way geometries are imported into the script by using the settings button that appears when positioning the pointer on top of the geometry on the Geometry Imports section. After clicking settings, a dialog box will be displayed (Fig. 11). Note that geometries can be imported as geometries, feature or feature collections and you can choose on the button that appears on the box. We will learn more about this in the next section. You can also modify the name and the color of the layer. Finally, you can click the lock button to lock and unlock the geometry, what can be useful to prevent edits or deletions by accident.
One of the most notable features of Google Earth Engine is the enormous amount of data available to be used. The database consists on about a petabyte-scale archive of publicly available remotely sensed imagery and other related data sets. It includes products from the most popular satellites such as Landsat, MODIS, National Oceanographic and Atmospheric Administration Advanced Very High Resolution Radiometer (NOAA AVHRR), Sentinel 1, 2, 3 and 5-P; and Advanced Land Observing Satellite (ALOS), and other data such as precipitation data, sea surface temperature data, CHIRPS climate data, topography data, and land cover data (Gorelick et al., 2017). This database increases daily as new satellite imagery is taken, processes and uploaded. New products are also being uploaded frequently. You can explore the data sets at the following link.
In addition to GEE, there is an online community working collaborating further to distribute open source data. New products and datasets are constantly being uploaded and can be find in the following link. Also, a weekly list of new products updated to GEE can be found in the following GitHub repository.
Now that we have a better understanding of the GEE layout, it is time to start coding. The API of GEE uses JavaScript coding language. This is an object-oriented language with first-class functions, and is best known as the scripting language for Web pages, but it’s used in many non-browser environments as well. It is a prototype-based, multi-paradigm scripting language that is dynamic, and supports object-oriented, imperative, and functional programming styles (xxx).
In this first section we will learn the basics of JavaScript coding. There are certain code statements that are proper of GEE. We will later dive into those specifics.
The code on this section can be found here
The code editor panel on the interface is probably the one that you will use the most. If this is the first time you are facing a coding language to perform data analysis, the recommendation is to take it slow. As you get familiarized with the code structure and functions, things will get easier, we promise.
Let’s start with a simple JavaScript statement. Go to the code.earthengine.google.com and type into the code editor panel:
print('This is my first code');
After, click Run. The text ‘This is my first code’ will appear on the Console tab. The parenthesis is used to pass arguments to functions. Here print is the function that prints the argument on the console.
In JavaScript, each statement ends in semicolon (But if you do not write it, the code will run anyway, but it may help you to keep your code organized). You will learn to write entire analysis that will be composed of multiple statements like that one.
It is really important that you keep your code organized and knit.
One way of doing this is by commenting your code statements. Add
// before the text you want to add after the statement. For
example:
print('This is my first code'); //This is my first statement to print text
JavaScript will read the first section of the statement, but will
omit all the text that comes after the //. You can also
comment out statements by placing // at the beginning of
it. This will prevent the statement from running without having to
delete it from the code.
//print('This is my first code');
A note of writing your script: As you continue working on your script, you will add new lines of code. In Earth Engine, every time you click run the entire code will be computed. If there is an error in some line, the computation will end up there and an error message will be prompted in the console pointing to the line number that has the error that stopped the code from continuing processing. You will need to debug and fix the error for the entire script to run successfully to the end.
JavaScript works with objects that you can store and later use in
other operations. You can save objects using var at the
beginning of the statement followed by a name for the objects. There are
several types of data types that JavaScript recognize and that we can
use: strings, numbers, list and dictionaries.
Strings are text that are defined by single or double quotes (but never mix them). Generally, it is preferred to use single quotes. You can make a string and store it as a new object called myfirststring and print it.
var myfirststring = 'This is my first code';
Numbers can also be saved as objects.
var number = 57;
Now we can pass this object to the function print. But
this time, we are giving two arguments to the function as follows:
print('My favorite number is:', number);
When looking in the console, now the string and the number have been printed.
Another useful data type are lists. Lists are defined using brackets
[]. For example, we can create a list of numbers.
var myfirstlist = [0,1,2,3,4,5,6];
print('This is my first list of numbers:', myfirstlist);
Or also a list of strings.
var list2 = ['a','b','c','d'];
print(list2)
We can also create arrays, that will add another dimension to the list. We use brackets to define each dimension. The first brackets define the columns of first dimension and the second brackets (inside the first ones) define the rows or second dimension.
var myfirstarray = [[1,2],[3,4],['a','b']];
print (myfirstarray);
Note that we have mixed numbers and strings here.
Dictionaries are composed of pairs of keys and values and they are
made using curly brackets {}. When defining a dictionary,
you need to specify the key (a name) and the value (e.g., a number, a
string, a list), separated by a colon. Add comma after the first pair of
key and value if you want to have more than one. For instance:
var myfirstdictionary = {
first: 1,
second: 'a string',
third: [1,2,3,5,8,12]
};
print (myfirstdictionary);
One important feature of dictionaries is that you can access the dictionary items suing square brackets or dot notation:
print(myfirstdictionary['first']);
print(myfirstdictionary.first);
Dot notations are handy in JavaScript coding architecture. We will use it more frequently when running different functions in a line of code.
Now that we have seen the different data types, we are going to start
building and using functions. Functions allow to perform a myriad of
operations. While, there are multiple functions that are already built
within Earth Engine (Remember the Code Editor Docs tab
to see all built-in functions), sometimes it is useful to create your
owns. Moreover, understanding the basic components of a function will
help you understand all the built-in ones. Functions need to be stored
as variables as before. Functions are defined with the keyword
function followed by parentheses within which you can
define different parameters on which the function will operate. After
the parentheses, follow the statements that make up the function and go
inside curly brackets. The last statement is return, which
indicates the output of the function.
var myfirstFunction = function(parameter1, parameter2) {
statement1;
statement2;
return statement;
};
There is no limit on the number of parameters that a function can take, and it can also use variables declared outside the function.
For example, we can build a function that adds 2 to a number. We will name the function add2
var add2 = function(number) {
var result = ee.Number(number).add(2);
return result;
};
print('Result of add2:',add2(4)); //Adds 2 to the number 4
So far, we have been using JavaScript code working with our computer
processor, also known as the client side. However, we are going
to be using Google computing infrastructure to run operations, rather
than the processor of your own computer, or the server side.
Thus, we need to create objects that can be sent in a form that is
understood by Google servers. These are the Earth Engine
objects. These are written in the form of ee.thing
with thing being a data type, also known as containers. Maybe, you
noticed that in the previous function, we specified
ee.Number so that Google Server could compute the sum.
Now on, in order to created variables, we are going to assign those variables to the corresponding container, which depends on the data type, so that Google can recognize it and operate on it.
For strings, we have the ee.String() container
var simpleString = 'Text to send to server';
var eeString = ee.String(simpleString);
print('This is:', eeString);
Now the print() function will send the container to Google server to be evaluated, and then is sent back to your computer, where the container is opened to be displayed.
Similarly, for numbers we use ee.Number(), for lists
ee.List() and so on. The ee.Thing are
constructors that take arguments and parameters to be processed by
Google servers and returns a container with a Earth Engine object that
you can manipulate, export, print or plot in your code.
Note that print, Map, Chart, and other function we will use are on the client side. They run on your computer, not on Google servers. WE will see next how to operate Google objects on the server side.
Now we have seen how to create objects that Google servers can read and process. It is important to incorporate this concept because it will be critical for developing more complicated codes.
Once you have created an Earth Engine object, then JavaScript operations will not work, instead you have to use operations defined for those Earth Engine objects. Thus, while we are using JavaScript to code, we are now going to use a specific set of functions created to operate on Google Earth Engine. On the Doc tab on the top-left panel, you can find all the methods and functions available to work with Earth Engine objects.
Within the built-in function set, you will find a variety of useful
functions that will make your work easier. For example, you can create a
sequence of numbers using ee.List:
var eeList = ee.List([1, 2, 3, 4, 5]);
But also you can use the built-in Earth Engine function sequence, to create the same variable:
var sequence = ee.List.sequence(1, 5);
Now that the ee.List has been saved as a variable, you
can use other Earth Engine functions to interact with this object. This
will be the building block of many functions that we will be using along
this tutorial. One important function is get, that gets
something from the ee.List object. For example, you can get
the second component of the list.
var value = sequence.get(2);
print('Value at index 2:', value);
One important aspect to consider when accessing variables created with Earth Engine functions is the data type. Many times, the object that returns from a specific function does not come with a data type assigned. Consequently, when trying to operate on those new variables, you will get an error stating that the argument is not a function. For instance, if you try to add a number to the value extracted from the sequence in the previous step, you will get an error saying value.add is not a function.
To avoid this problem, you need to reassign the corresponding
container for the specific data type, in this case a
ee.Number:
print(ee.Number(value).add(5));
This process is known as casting.
Dictionaries can be really useful in Earth Engine. Similar to the
JavaScript dictionary, we can create Engine containers for dictionaries
using ee.Dictionary and specifying the different keys. Lets
create a dictionary containing keys for a species information:
// Create an Earth Engine dictionary
var dictionary = ee.Dictionary({
Common_name: 'Tiger',
Genus: 'Panthera',
Species: 'Panthera tigris'
});
// Get the species name from the dictionary.
print('Species:', dictionary.get('Species'));
// Get all the keys:
print('Keys: ', dictionary.keys());
Note that with the Earth Engine dictionary, now you cannot access the
elements using JavaScript code as before. You need to use an Earth
Engine function with Earth Engine objects. To extract a key you need to
use the function get. Remember, as we explained before, the returning
objects does not have an data type specified, and you will need to
specify ee.String to use the object in another Earth Engine
function. Finally, you can also use the function keys to
extract all keys, and the resulting objects is going to be an
ee.List.
Something that can be handy to remember before we move to more
complicated operations and data manipulation in Google Earth Engine, is
that you can pass argument to functions by name, using a similar
structure to a dictionary. In this way, you can visualize the name of
the different parameters, what can help understand the function. You can
write the parameters in any order. For instance, the function
Map.setCenter() centers the map view at a specific zoom
level. It then takes tree arguments, lat, long and zoom. You can run the
function as follow:
Map.setCenter(99.2, 12.6, 6);
Or you can pass the arguments using a dictionary to visualize the name of the arguments:
Map.setCenter({lon:99.2, lat:12.6, zoom:6});
The arguments by name can be in any order:
Map.setCenter({lon:99.2, zoom:6; lat:12.6});
Many times you will find yourself working with data and imageries
from different times that you will need to filter. To manage time in
Google Earth Engine, you need to use date Earth Engine objects using
ee.Date. You can construct an ee.Date object
using a string to specify the date:
// Define a date in Earth Engine.
var today = ee.Date('2020-6-9'); //This is a year/month/day format
print('Today is:', today);
You can also use another less orthodox method that is using a JavaScript Date that represents milliseconds since midnight on January 1, 1970. This seems strange at first, but you will see later on that it becomes handy for multiple operations.
// Get current time using JavaScript Date
var now = Date.now();
print('Milliseconds since January 1, 1970', now);
Later in this tutorial, we will explore different functions to filter data sets by date or use data in more complex analysis.
We saw how to package objects created in the client side
into containers to be used on the server side, the ee.Thing.
Sometime, however, you may need to convert earth engine objects into
client-side objects. In this case, you can open the container to access
its information using .getInfo(). Another method is using
the function evaluate(), which retrieves the values inside
the earth engine objects. This is strictly not recommended by Google, so
you need to use only when it is absolutely necessary. The difference
with .getInfo() is that the later will stop the rest of the
code from continuing processign until all information is retrieved from
the Earth Engine object, whereas evaluate() will allow
other porcesses to continue operating.
Retrieving the information in the containers and mixing the client and server side is not recommended by Google, and you should only use it when absolutely necessary. It is always better to operate on the server side. Check the Google user guide to learn more about this.
We have seen so far some important data structures in Earth Engine, String, Number, Array, List, Dictionary and Date. Those are fundamental blocks of Earth Engine code. However, in Google Earth Engine we will be working with geospatial data, thus, two other important geographic data structures are Image and Feature. These two data structures correspond to the raster and vector data types. Images are composed by a combination of bands and a dictionary with properties and features by a combination of a geometry and dictionaries with properties. If you are not familiar with raster and vector data, this is a good moment to refresh those concepts before you continue with the next section.
In this first section we have cover an overview of the Google Earth Engine platform. By now, you should have an account set and be familiarized with the web-based interface. We have also seen the basics of JavaScript coding language and how it works within Earth Engine. In the following section we will start using some data and learn how to work with it.
For a more in depth description and tutorials to use GEE, remember to visit the Google Earth Engine guide and tutorials.
Gorelick, N., Hancher, M., Dixon, M., Ilyushchenko, S., Thau, D., & Moore, R. (2017). Google Earth Engine: Planetary-scale geospatial analysis for everyone. Remote Sensing of Environment, 202, 18–27. https://doi.org/10.1016/j.rse.2017.06.031
C.E. Woodcock, R. Allen, M. Anderson, A. Belward, R. Bindschadler, W. Cohen, F. Gao, S.N. Goward, D. Helder, E. Helmer, R. Nemani, L. Oreopoulos, J. Schott, P.S. Thenkabail, E.F. Vermote, J. Vogelmann, M.A. Wulder, R. Wynne. 2008. Free access to Landsat imagery. Science, 302 (5879), p. 1011
Michael A. Wulder, Thomas R. Loveland, David P. Roy, Christopher J. Crawford, Jeffrey G. Masek, Curtis E. Woodcock, Richard G. Allen, Martha C. Anderson, Alan S. Belward, Warren B. Cohen, John Dwyer, Angela Erb, Feng Gao, Patrick Griffiths, Dennis Helder, Txomin Hermosilla, James D. Hipple, Patrick Hostert, M. Joseph Hughes, Justin Huntington, David M. Johnson, Robert Kennedy, Ayse Kilic, Zhan Li, Leo Lymburner, Joel McCorkel, Nima Pahlevan, Theodore A. Scambos, Crystal Schaaf, John R. Schott, Yongwei Sheng, James Storey, Eric Vermote, James Vogelmann, Joanne C. White, Randolph H. Wynne, Zhe Zhu. 2019. Current status of Landsat program, science, and applications, Remote Sensing of Environment, 225, 127-147, https://doi.org/10.1016/j.rse.2019.02.015.
In this introductory module we have covered the very basics of programming in Google Earth Engine. In the next module, we will start processing spatial data like images and features. This assignment is an opportunity to internalize some of the basic JavaScript functions that you will build on as you continue to grow your knowledge of GEE.
Here are some exercises that will help you practice your coding skills. For the assignment, you need to create a single functional script with both code and responses to questions. You can add any answers to the questions into your script using comments //
In the code editor panel of the Google Earth Engine interface, use JavaScript code to create 3 different objects. You can give any name to these objects, but each object needs to be a different data type. Print your objects in the console by clicking the run button and make sure your code runs without errors.
Here is a list of JavaScript code commands. Each line of code contains an error (You can run the line of code to see the error). Find the error and fix it, so the code runs successfully.
print(this is my first code);
print(4 + “4”);
var mylist = [0,1,2,a,d,c];
print(mylist)
var number = 45;
print(Number)
var mysecondlist [1,2,4];
print(mysecondlist)
var mydictionary = [
first: 1,
second: 'a string',
third: [1,2,3,5,8,12]
];
print (mydictionary);
Using the search tab on the Docs panel, find the function add for ee.Number objects. Using this function, add 5 to 5 and store the result as a new variable. Print this variable to the console.
It is important that you understand the difference between JavaScript objects and Earth Engine objects. As an exercise, transform the following JavaScript objects into their equivalent Earth Engine object (the ee.Thing).
var animal = ‘Tiger’;
var number = 78;
var list = [1,2,3];
var multiplyby2 = function(number) {
var result = number.multiply(2);
return result;
};
print('Result:', multiplyby2(4));
In this second section we will learn how to import, operate and display images and features on Google Earth Engine (GEE). This is the basic building block for any ecological spatial analysis and with time, this set of operations and functions will become a simple habit.
In order to learn the basic procedures and functions to work in GEE, we will develop a simple example. The idea is that after completing this section, you will have a basic understanding on how to change the area of interest, load and work with different data sets (both, images and features) and perform some of the most common functions and operations.
The code on this section can be found here
The first step in any project that involves coding is to start and save a script where you can safely keep your work progress. First, using the forward slash to comment out code, write a header to your script, providing general information such as, your name and a title. Something like this:
/////////////////////////////////
// My name
// Getting started with GEE
/////////////////////////////////
You can also comment chunks of code using a slightly different code:
/* text */ This can be more handy for when you need to
activate and deactivate several lines of code at the same time.
/*
My name
Getting started with GEE
*/
You can choose the method that is easier for you.
After you completed the header, click on the New button on the Scripts tab of the left panel. From the dropping menu, chose File. This will open a window where you can provide a name and chose the repository where to save your script. Give scripts proper names. You can also provide a brief description of your project if desired. Once you have saved the script for the first time, you just need to click Save (Above the code editor panel) every time you want to save updates to the script (Fig. 1).
As you may recall from the introductory section, you can also create new repositories and folders to keep your work organized and share them with collaborators.
The study area for our example is in southeast Kenya. We will use this area to learn the basics of data manipulation in GEE, but you can pick any place on the globe.
To position the map display on the area of interest, we will use the
function Map.setCenter(), for which we need to provide
coordinates and a zoom level (higher numbers indicate larger scale or
more zoomed in). You may recall this from the previous section.
Map.setCenter({lon:35.65, lat:-1.54, zoom:6});
The next step is to define a geometry, in this case a rectangle, that will represent the study area. Click on the rectangle icon from the geometry tools and draw a rectangle that covers the area of interest (see Fig. 2). We need to provide a name to the new geometry. Name it StudyArea.
Hint: You cannot use spaces for names, so we use capital letters or underscores to differenciate words. At the end, it does not matter what name you use, but it is good to use names for variables that you can recognize.
After you finish, the geometry will appear in your imports (top of the script editor) as a polygon with four vertices. We will discuss more about geometries later in this section.
You can rename the geometry and save it as a new object using code:
var Bounds = StudyArea;
You have now defined the study area. Later, we will use this polygon to clip images and restrict the extent of our analysis.
In GEE, raster data are represented as Image objects and are the main type of data to work with. All images are composed on one or more bands, where each band has a name, data type, scale, mask and projection, as well as metadata stored as a set of properties. Let’s start working with some simple images.
The first step for our workflow is to import an image. Let’s start working with a simple image with one band which contains information about elevation, also known as Digital Elevation Models.
Remember from previous section that you can search for places, images, image collections, and feature collections on the GEE Data Catalog.
To find the elevation data, on the search bar type: ‘SRTM Digital Elevation Data 30m.’ Now, click on it to display the data description where you can find information about the temporal availability, data provider and collection ID (Fig. 3). This data set corresponds to a digital elevation data from the Shuttle Radar Topography Mission (Farr et al. 2007) and contains information of elevation at 30m spatial resolution. You can click import to add it to your imports, or you can use code. We encourage you to use code, so you get more familiarized with it and also helps you to keep the work organized.
The function ee.Image() allows you to import image
catalogs. To import the elevation data, we need to provide the directory
to the data within the parenthesis of the function. Remember to assign
the image to a new object using var as follows:
var Elev = ee.Image("USGS/SRTMGL1_003");
You have just imported the elevation data for the entire world.
When working in spatial ecology, frequently, slope, aspect and hillshade are variables of interest as many species respond positively or negatively to changes in the terrain surface. Thus, these data sets can be good predictor variables in several models. We only have information about elevation, but you can calculate slope, aspect and hillshade and save them as new objects using other GEE built-in functions.
To calculate all these terrain variables, we will use the built-in
function ee.Algorithms.Terrains().
Let’s apply the function to the elevation image and save it as a new object.
var Terrain = ee.Algorithms.Terrain(Elev);
At this point, you should be familiarized with the code syntax to create objects and use built-in functions in GEE.
Print both elevation images to display the information on the console.
print(Elev);
print(Terrain);
You will see that differently from the Elev image that contains one
band, the new image created (Terrain) contains four bands, one for each
variable: elevation, slope, aspect and hillshade. All of these three new
bands where calculated from the elevation data. If you want to know more
about the algorithms used to obtain these results, remember that for all
the Engine built-in functions you can use the API reference on the Docs
tab to see a description and all the parameters used by the function.
The ee.Algorithms category contains a list of currently
supported algorithms for specialized or domain specific processing.
Look for the ee.Algorithms.Terrains() function in the
Docs tab (Fig. 4).
This exercise shows you the versatility and power of conducting spatial analysis on GEE. You can in matters of seconds, download elevation data and calculate slope, aspect and hillshade for the entire world!
In many cases, we are only interested in one specific area and not
the entire planet. We can use the GEE built-in function
clip() to crop the image to our area of interest, the
polygon we crated earlier.
Using the dot notation, we add the function clip to the
image object you want to crop, and specifying in between parenthesis the
object representing the area of interest we previously created.
var Terrain = Terrain.clip(Bounds);
You have now an image object cropped to the area of interest.
Often, you will be interested in visualizing the images. To display
the elevation data on the map, you can use the function
Map.addLayer(). To the previous code, now add the function
to display the data as follows:
Map.addLayer(Terrain);
Parameters for functions may have default values, and then, you only need to specify the parameter if you want to change its default value. For instance, the previous function displays a gray surface. You need to change the default parameters for the appearance of the image to be able to visualize elevation change.
In the case of the Map.addLayers() function, it contains
five arguments (Fig. 5). The first argument is the ee.Object to display,
in our case the Elev object. The second argument are
the visualization parameters, which you can pass to the function as a
dictionary. Those include the bands to display (when you have more than
one band in the image, such as the Terrain image created that has 4
bands), the minimum and maximum values to display, and colors (Remember
that you can also change those parameters from the Layers button in the
display). The third argument is the name of the layer (a string). The
fourth argument is a Boolean number (i.e., 1 for TRUE or 0 for FALSE)
and indicates whether the layer should be displayed by default or be
activated by the user manually by clicking on the layers tab. The last
argument in the opacity, which goes from 0 (transparent) to 1.
Map.addLayer(Elev, {min: 0, max: 3000}, 'DEM', 1);
Map.addLayer(Terrain, {bands:['elevation'] ,min: 0, max: 3000}, 'Elevation', 1); //Same as before but from the terrain object
Map.addLayer(Terrain, {bands:['slope'] ,min: 0, max: 10, palette:'white,red'}, 'Slope', 1); //Display slope and add a color palette
Note the difference between an image that was cropped and the one that was not cropped. Play with the image visualization using the Layers button in the display.
We have so far worked with a single image. Now, we are going to access an entire image collection. Image collections can contain hundreds of images across long periods of time, such as images from satellites. Similarly to the elevation image, you can search for image collections. Let’s access the image collection of Landsat 8. Landsat images come with different levels of pre-processing. Here we will work with T1, that is the highest level of pre-processing.
Use the search bar to find the name of the Landsat 8 T1 image
collection and import it using the function
ee.ImageCollection().
var landsat8 = ee.ImageCollection("LANDSAT/LC08/C02/T1")
You just imported the entire image collection of Landsat 8 for the entire Earth land surface. You could try to print the image collection to see all the images contained in the list. However, because there are too many images, printing it will either be very slow, time out, or return an error.
What we need to do to work with image collections is to filter them and reduce the number of images in the list to the ones that we are interested in. To do this, we will use filters.
Filters are a set of functions that allows you to select a set of images based on certain parameters such as position, time windows, numbers, properties, depending on the type of object you are trying to filter, such as image collections, feature collections, lists or geometries. Lets see how this works for image collections.
Let’s start by filtering our image collection by space and by time,
specifying the area and the time frame in which we are interested in
(Remember different satellite products contain images for different time
periods and that information is provided in the data description of each
product). We need to use filterBounds() to select images
from our area of interest (the object Bounds) and
filterDate() to keep only images from 2019, thus specifying
a time window between 2019-01-01 and 2019-12-31. Note how we can add
functions to the same line of code using the dot notation.
var landsat8 = ee.ImageCollection("LANDSAT/LC08/C02/T1")
.filterBounds(Bounds)
.filterDate('2023-01-01', '2023-12-31')
The new object contains only images for which the path overlaps our area of interest and that were obtained during 2019.
In addition, we are interested in finding images that are cloud-free.
Here we will use another function to solve this problem. All Landsat
images contain information about cloud cover in the metadata. We can use
this information to sort the image collection from lowest cloud cover to
highest cloud cover using the function sort(), which
operates on the image metadata. You could apply a new function to the
object created with the previous line of code. You can also simplify
things by concatenating functions in the same line of code, again using
the dot notation.
var landsat8 = ee.ImageCollection("LANDSAT/LC08/C02/T1")
.filterBounds(Bounds)
.filterDate('2023-01-01', '2023-12-31')
.sort('CLOUD_COVER', true);
print(landsat8) //The image collection contains 106 images
The new image collection is now sorted by the cloud cover percentage, from lowest to highest. We also see that the collection contains now 240 images.
You can save the first image (lowest cloud cover) as a new object and display it. This time, you are displaying an image composed by a series of bands and the final color display depends on the combination of bands assigned to the tree main colors, red, green and blue. If you assign the bands corresponding to the three colors, then you obtain a true color display. You can also try a false color composition or displaying just the thermal band.
var first_image = landsat8.first();
print(first_image)
// Display image
// True color
Map.addLayer(first_image, {bands: ['B4', 'B3', 'B2'], min: 7000, max: 13000}, 'Landsat8-RGB', 1);
// False color
Map.addLayer(first_image, {bands: ['B5', 'B4', 'B3'], min: 7000, max: 13000}, 'Landsat8-FalseColor', 1);
// Thermal
Map.addLayer(first_image, {bands: ['B10'], min: 27000, max: 37000, palette: ['blue', 'red', 'orange', 'yellow']}, 'Landsat8-Thermal', 1)
As always, you can access the complete GEE filtering functionality by
tipping ee.Filter into the search bar of the Docs
tab.
We have so far selected and printed the first image of the list. Now
we want to create a full mosaic of the study area with images that are
free of clouds. The reduce function allows to reduce image collections
to a single image. It is a way to aggregate data over time
(imageCollection.reduce()), space
(image.reduceRegion(),
image.reduceNeighborhood()) or bands
(image.reduce()).
The ee.Reducer() can be a used to operate a simple
statistic for aggregating data (e.g. minimum, maximum, mean, median,
standard deviation, etc.), or for more complicated analysis on the input
data (e.g. histogram, linear regression, list). In all these cases, the
reducer takes an input data set and calculates a single output. When a
single input reducer is applied to a multi-band image, such as Landsat
image, GEE automatically applies the reducer separately to each band,
producing an output image with the same number of bands as the
input.
Continuing with our example, this time we will select all images with
less than 10% cloud cover and estimate the median value for each pixel
and each band in the Landsat 8 collection. To retain only images with
<10% cloud cover we will use the function,
filterMetadata() that requires three arguments: The name of
the metadata property, the name of a comparison operator (it can be:
“equals”, “less_than”, “greater_than”, “not_equals”, “not_less_than”,
“not_greater_than”, “starts_with”, “ends_with”, “not_starts_with”,
“not_ends_with”, “contains”, or “not_contains”) and a number for the
cloud cover percentage to compare against. We will also increase the
time period to include images from the first day of 2015 to the last day
of 2019 to obtain a five years mosaic of our study area.
var l8_mosaic_1 = ee.ImageCollection("LANDSAT/LC08/C02/T1")
.filterBounds(Bounds)
.filterDate('2023-01-01', '2023-12-31')
.filterMetadata('CLOUD_COVER', 'less_than', 10)
.reduce(ee.Reducer.median());
print(l8_mosaic_1)
The reducer is going to change the name of the bands. When printing the image, you can see that the band names have changed (Fig. 6).
Instead of using the reducer, you can use the built-in function
median() that will do the same operation on each pixel
across the stack of all matching bands but retain the original band
names.
var l8_mosaic_2 = ee.ImageCollection("LANDSAT/LC08/C02/T1")
.filterBounds(Bounds)
.filterDate('2023-01-01', '2023-12-31')
.filterMetadata('CLOUD_COVER', 'less_than', 10)
.median();
Map.addLayer(l8_mosaic_2, {bands: ['B4', 'B3', 'B2'], min: 7000, max: 12800}, 'Landsat8-Mosaic', 1);
As we develop more complex analysis along the tutorial, we will learn
more specifics on the reducer functions. You can look into the
ee.Reduce methods that exist in the API.
Another powerful functionality is performing mathematical operations on images or image bands. For example, you can use a combination of bands to calculate vegetation indexes. One of the most widely used vegetation indexes is the Normalized Difference Vegetation Index (NDVI). This index provides an estimation of vegetation productivity, thus, is widely used in spatial ecology studies. It is calculated using the near infrared and red bands as follows:
*NDVI = (NIF−RED)/(NIR+RE**D*)
To demonstrate the versatility of GEE to perform mathematical operations on images, we will calculate NDVI using different methods.
Some simple image math can be performed by using add(),
subtract(), divide(), multiply(),
pow(), etc. Those operators can be applied on numbers,
images or arrays. We can use these operators to calculate NDVI.
First, we need to select the desired bands from the
image and then apply the mathematical operation. For our example, we
need the red band (band 4) and the near infrared band (band 5):
// Method 1 - Applying band operations
var ndvi_1 = l8_mosaic_2.select('B5').subtract(l8_mosaic_2.select('B4'))
.divide(l8_mosaic_2.select('B5').add(l8_mosaic_2.select('B4')));
However, it is generally the case that you need to perform more
complicated mathematical operations. For those cases, you can use the
expression() function, which allows to represent math
operation in text forms.
// Method 2 - Applying band opperations using a expression
var ndvi_2 = l8_mosaic_2.expression('(B5 - B4) / (B5 + B4)', {
B5: l8_mosaic_2.select('B5'),
B4: l8_mosaic_2.select('B4')
});
Note that expression() returns an integer if two
integers are divided, so that the expression 10 / 20 = 0. To obtain
decimal numbers as results, you need to have decimal numbers in the
operations. For instance, in the previous case, you have to multiply one
of the operands by 1.0: 10 * 1.0 / 20 = 0.5.
For many basic operations, such as calculating vegetation indexes,
often exists a built-in function in Earth Engine that makes things much
easier. Here, we will use the normilizedDifference()
function. We need to provide the name of the two bands to use.
// Method 3 - Using a built-in function
var ndvi_3 = l8_mosaic_2.normalizedDifference(['B5', 'B4'])
Finally, it may be the case that you want to write your own function.
The advantage of writing a function is that you can then apply the
function to any image you want to work with. Here, we are going to write
a function that calculated NDVI (function
normilizedDifference()), renames the resulting band as NDVI
(function rename()) and adds it to the image as a new band
(function addBands()).
// Method 4 - Building your own function to compute NDVI and add it as a new band to the image
var addNDVI = function(image) {
var NDVI = image.normalizedDifference(['B5', 'B4']).rename('NDVI')
return image.addBands(NDVI) //with addBands, the new NDVI is added as a new band to the existing bands.
};
// Apply the function to the mosaic
var l8_mosaic_3 = addNDVI(l8_mosaic_2);
You can also use the function map() to apply the addNDVI
function you have created to an entire image collection. As a result,
all the images on the collection will have an additional band called
NDVI.
var l8_NDVI = ee.ImageCollection("LANDSAT/LC08/C02/T1")
.filterBounds(Bounds)
.filterDate('2023-01-01', '2023-12-31')
.filterMetadata('CLOUD_COVER', 'less_than', 10)
.map(addNDVI);
Another common operation when working with images is masking. Masking refers to setting certain pixels from an image to no data values (i.e., making them transparent) in order to exclude them from analyses. Masking is usually done to remove pixels with poor data, representing clouds or any other area that wants to be excluded.
Every pixel in a band of an ee.Image, in addition to its
value, has a mask which ranges from 0 (i.e., no data) to 1. In Earth
Engine, all masked pixels (0) are treated as no data. When applying a
mask, pixels with a value of 0 are then excluded from operations. For
instance, when applying image1.mask(image2), the values of
image2 are taken and used as a mask of image 1, meaning that pixels in
image2 that have the value 0 will be made transparent in image1.
Continuing with our example, we may be interested in removing all the ocean water from the NDVI image. We need to create a mask that will retain only the land pixels (Fig. 7).
Using the elevation data, we can use the function gt()
(grater than) to create a binary image with a value of 1 for all pixels
with elevations greater than 0, and a value of 0 to pixels with values
lower than 0. Then we use the function updateMask() to
retain only NDVI values from the land (i.e., elevation > 0).
// Create a land mask using SRTM elevation data.
var watermask = ee.Image("USGS/SRTMGL1_003").gt(0);
// Update NDVI mask with the land mask.
var maskedndvi_1 = ndvi_1.updateMask(watermask);
// Display the masked result.
Map.addLayer(maskedndvi_1, {min:0, max:1}, 'NDVI masked');
Masks can be particularly useful in cases where you cannot find images that are cloud free and you need to mask those pixels for analysis.
In addition to the raster datasets, we can use vector data in GEE. Vector data is handled with the Geometry type. Geometries in Earth Engine can be Point (a list of coordinates in some projection), LineString (a list of points), LinearRing (a closed LineString), and Polygon (a list of LinearRings where the first is a shell and subsequent rings are holes). MultiPoint, MultiLineString, and MultiPolygon are also supported.
You can create Geometries using the Code Editor geometry tools, as you did at the beginning of the section to define your study area. However, you can also use code to create geometries, by providing a list of coordinates. The following code will create the polygon representing the study area defined earlier. All we need is a list of pair of coordinates, indicating the vertices of the polygon. Check that the first and last coordinates are the same.
var polygon = ee.Geometry.Polygon([
[[97.58, 9.32], [100.43, 9.32], [100.43, 14.43], [97.58, 14.43], [97.58, 9.32]]
]);
Geometries can then be converted into features. A Feature is an object with a geometry property storing a Geometry object (or null) and a property storing a dictionary of other properties. In order to create a feature, you need to provide a geometry, but also a dictionary with other properties of interest.
var polyFeature = ee.Feature(polygon, {Area: 'Tanintharyi'});
Geometry and Feature objects can be
printed or added to the map similarly to images using
Map.addLayer(). The default visualization parameters will
display vectors with solid black lines and semi-opaque black fill. You
can change the colors similarly to images.
print(polyFeature);
Map.addLayer(polyFeature, {}, 'Study area');
Map.addLayer(polyFeature, {color: 'red'}, 'Study area - red');
Similar to image collections, you can create a feature collection,
that is as the name implies, a collection of features. To do this, we
need to apply ee.FeatureCollection() on a list of features.
For instance, we can create a list of feature points, each of which
represents a town with its coordinate (the geometry) and a dictionary
with the name of the town. We can then combine all the town into a
feature collection.
// Make a list of Features
var feature_list = [
ee.Feature(ee.Geometry.Point(98.62, 12.44), {name: 'Myeik'}),
ee.Feature(ee.Geometry.Point(98.76, 11.25), {name: 'Bokpyin'}),
ee.Feature(ee.Geometry.Point(98.52, 13.39), {name: 'Pe Det'})
];
// Create a FeatureCollection from the list, print it and display it on the map
var Towns = ee.FeatureCollection(feature_list);
print(Towns);
Map.addLayer(Towns, {color: 'blue'}, 'Towns');
Google Earth Engine offers several Feature collections through the Data Catalog that can be imported. They are mostly data sets for the USA but, in addition to the protected areas we used, there are also country boundaries and ecoregions, among others. This is a much smaller catalog than the one for image collections, but the amount of data available grows every day.
Feature collection can be large, and similar to image collections, we may need to filter them. We will also use filters to do this. Here, we will load the World Database on Protected Areas and filter it to our study area, similarly to what we did before to filter the Landsat image collection.
var PA = ee.FeatureCollection('WCMC/WDPA/current/polygons');
var PA_filtered = PA.filterBounds(Bounds);
You can now check how many protected areas are within the study area
by using size() which returns the number of entries in a
dictionary.
print('Count after filter:', PA_filtered.size());
Now, let’s filter the protected areas on our study area that are
Marine National Parks. We can use the ee.Filter.eq() (eq
for equal) function to retain only those protected areas designated as
Marine National Park and check how many there are. As with the
filterMetadata, you need to provide the property name of
the feature to filter on and the value to compare against.
var MarineNatParks = PA_filtered.filter(ee.Filter.eq('DESIG', 'Marine National Park'));
print('Number of Marine National Parks:', MarineNatParks.size());
Similar to what we learned about mapping functions over image collections, we can do it on feature collections. Let’s say you are interested in calculating the area of each Marine National Park. We will do this as an exercise even though the Protected Area database already has a property with the area of each feature.
First, we define a function to apply to each feature. The function
calculates the area using the area() function. We then
divide this by 1000000 to obtain area in square kilometers. The
resulting number is set to each feature as a new property that we called
areakm2.
Second, we map the addArea function we created across
the MarineNatParks feature collection.
// Compute the area of each National Park in km2
// Function to compute patch area and perimeter and add it as a property
var addArea = function(feature) {
return feature.set({areakm2: feature.area().divide(1000 * 1000)});
};
// Map the area using the function over the FeatureCollection
var Area = MarineNatParks.map(addArea);
We can also apply reducers to feature collections. The idea is the
same than for image collections, aggregate data over the collection.
Here, we use the reduceColumns() function on the Area
feature collection previously created. We need to specify the reducer we
want to apply, in this case the ee.Reducer.mean(), and the
property or list of properties we want to reduce, in this case
areakm2. When printing this object we have a new property
with the mean area of all National Parks in the collection.
// Calculate the mean area across parks
print('Mean National Park area (km2):', Area.reduceColumns(ee.Reducer.mean(), ['areakm2']));
Finally, you can display the protected areas and Marine National
Parks on the map. We use the same function than more images,
Map.addLayers().
Map.addLayer(PA_filtered, {color: 'green'}, 'Protected Areas');
Map.addLayer(MarineNatParks, {color: 'blue'}, 'Marine NP');
Lastly, at the end of an analysis you may want to export your results and data products. There are many reasons you may want to do this. The most obvious is to create figures and maps using another software, such as QGIS or to use raster and vector products in other analysis in software such as R.
There are different type of products that can be exported from GEE. These include images, map tiles, features, tables and videos. The data can be exported to your linked Google Drive account, as a new Earth Engine asset that will appear on your asset manager, or to Google Cloud Storage (Note this is a fee-based service). We will not cover Cloud Storage in this tutorial, but you can learn about Cloud Storage here and how to set projects here.
Here, we are going to demonstrate how to export an image and a feature collection. We will see more about these functions along the different sections.
Imagine that you want to create a map of your study area displaying
the NDVI values across the region. You can then export the final NDVI
image we created previously maskedndvi_1. We will
export the image to Google Drive, where it can be downloaded to be used
in other software. For this, we will use the function
Export.image.toDrive().
All export functions have a series of arguments that we need to
complete. First, we need to specify the image we want to
export, in our case the maskedndvi_1. Second, we need
to provide a description that will be the file name when
saved in Google Drive. Then we need a scale parameter, the
spacial resolution or pixel size in meters, and a region, a
ee.Geometry object that defines your area of interest. In our example,
we have been using Bounds. Another important argument
is the maxPixels. The default is 1e8 pixels, but you can
increase this number as needed. Note that when the image is too big, it
will be exported in tiles as 2 or more images that then you can combine.
The image default output is GeoTIFF, but the
TFRecord format is also supported.
There are several other optional arguments for this functions. Here
we show two extra arguments. The folder within your Google
Drive to keep it organized (By default the image will be saved in the
Drive root directory) and the crs. The coordinate reference
system is optional, but we include it here because it is important to
keep track of the crs used when exporting data to use in other GIS
software.
Here is the code:
// Export raster images to Google Drive
Export.image.toDrive({
image: maskedndvi_1, // Export the masked NDVI as an example
region: Bounds,
description: 'NDVI-30m',
folder: 'GIS',
scale: 30,
fileFormat: 'GeoTIFF',
maxPixels: 1e10,
crs: 'EPSG:4326'
});
In other cases, you may be interested in saving an image as an asset,
so you can use it later in other analyses. To do this, we will use the
function Export.image.toAsset(). The difference with
exporting to Google Drive is that we now need to provide an
assetId as an extra parameter and that we do not need to
specify a file format anymore.
// Export raster image to asset
Export.image.toAsset({
image: maskedndvi_1, // Export the masked NDVI as an example
region: Bounds,
description: 'NDVI-30m',
assetId: 'NDVI30mExport',
scale: 30,
maxPixels: 1e10,
crs: 'EPSG:4326'
});
There are other optional arguments that can be modified. Look into the Export of the Docs tab to explore other arguments.
Similar to images, we can export feature collections or tabular data,
such as a future collection with no geometry, as CSV, ESRI Shapefile
(SHP), GeoJSON, KML, KMZ or TFRecord formats. Again, we can export to
Google Drive using the function Export.table.toDrive() or
as an asset export.table.toAsset(). The arguments that we
need to specify include the collection to export and a
description with the name for the resulting file. When
exporting to Google Drive we also need to provide the
fileFormat and we will also specify a folder
in Google Drive. When exporting as an asset, we need to provide the
assetId.
Here we will export the marine national parks (the object MarineNatParks) as a shapefile and also save it as an asset.
// Export feature to Google Drive
Export.table.toDrive({
collection: MarineNatParks, // Export the marine parks as an example
description: 'Marine_Parks_Tanintharyi',
fileFormat: 'SHP',
folder: 'GIS'
});
// Export feature to asset
Export.table.toAsset({
collection: MarineNatParks, // Export the marine parks as an example
description: 'Marine_Parks_Tanintharyi',
assetId: 'MarineParksExport'
});
// Export table data
Export.table.toDrive({
collection: Area,
description: 'exportMarineNationalParkArea',
fileFormat: 'CSV',
folder: 'GIS',
selectors: ['areakm2'] //A list of properties to include in the export; either a single string with comma-separated names or a list of strings.
});
Finally, we will export the Marine National Park area as table data
in a CSV format. This object will not have a geometry associated, only
the area we calculated for each feature. For this we use the
Export.table.toDrive() we used before with the difference
that now the fileFormat is CSV. We also use the
selectors argument to specify the property we want to
export, NAME and areakm2 of each park. You can add other properties if
wanted.
// Export table data
Export.table.toDrive({
collection: Area,
description: 'exportMarineNationalParkArea',
fileFormat: 'CSV',
folder: 'GIS',
selectors: ['NAME','areakm2'] //A list of properties to include in the export; either a single string with comma-separated names or a list of strings.
});
Note that to complete the export process you need to submit the task
by clicking the run button on the Tasks tab on the right
panel (Fig. 8). When the process is completed, the taks changes to a
blue color as shown in Fig. 8.
You will see the csv file in your Google Drive (Fig. 9).
So far, we have seen some common functions to apply on images and features. There are an enormous amount of possible operations that can be applied to filter collections, calculate new bands, etc. As we progress with different types of analyses, you will get more familiarized with the different possible operations.